package nape.shape;
import zpp_nape.Config;
import zpp_nape.Const;
import zpp_nape.util.Flags;
import DummyNapeMain;
import zpp_nape.util.Math;
import zpp_nape.util.Names;
import zpp_nape.util.Pool;
import zpp_nape.util.Lists;
import zpp_nape.shape.Circle;
import zpp_nape.shape.Edge;
import zpp_nape.space.Space;
import zpp_nape.shape.Polygon;
import zpp_nape.shape.Shape;
import zpp_nape.phys.FeatureMix;
import zpp_nape.phys.FluidProperties;
import zpp_nape.phys.Material;
import zpp_nape.phys.Body;
import zpp_nape.geom.AABB;
import zpp_nape.geom.Distance;
import zpp_nape.geom.Mat23;
import zpp_nape.geom.Vec2;
import zpp_nape.geom.VecMath;
import zpp_nape.geom.Collide;
import zpp_nape.dynamics.Arbiter;
import zpp_nape.dynamics.Contact;
import zpp_nape.dynamics.InteractionFilter;
import zpp_nape.constraint.Constraint;
import zpp_nape.dynamics.SpaceArbiterList;
import zpp_nape.constraint.Matrices;
import zpp_nape.constraint.PivotJoint;
import zpp_nape.constraint.WeldJoint;
import nape.util.Flags;
import nape.util.Lists;
import nape.util.Debug;
import nape.space.Space;
import nape.shape.Circle;
import nape.shape.Polygon;
import nape.shape.Edge;
import nape.phys.FluidProperties;
import nape.phys.Material;
import nape.phys.Body;
import nape.geom.AABB;
import nape.geom.Geom;
import nape.geom.Mat23;
import nape.geom.Vec2;
import nape.dynamics.Contact;
import nape.dynamics.Arbiter;
import nape.dynamics.InteractionFilter;
#if swc@:keep#end class ShapeType{
    public function new(){
        if(!ZPP_Flags.internal)throw "Error: Cannot instantiate "+"ShapeType"+" derp!";
    }
    @:keep inline public function toString(){
        if(false)return "";
        
        else if(this==CIRCLE)return"CIRCLE";
        else if(this==POLYGON)return"POLYGON";
        else return "";
    }
    public static var CIRCLE(flibget_CIRCLE,never):ShapeType;
    inline static function flibget_CIRCLE(){
        if(ZPP_Flags.ShapeType_CIRCLE==null){
            ZPP_Flags.internal=true;
            ZPP_Flags.ShapeType_CIRCLE=new ShapeType();
            ZPP_Flags.internal=false;
        }
        return ZPP_Flags.ShapeType_CIRCLE;
    }
    public static var POLYGON(flibget_POLYGON,never):ShapeType;
    inline static function flibget_POLYGON(){
        if(ZPP_Flags.ShapeType_POLYGON==null){
            ZPP_Flags.internal=true;
            ZPP_Flags.ShapeType_POLYGON=new ShapeType();
            ZPP_Flags.internal=false;
        }
        return ZPP_Flags.ShapeType_POLYGON;
    }
}
#if swc@:keep#end class ShapeList{
    public var zpp_inner:ZPP_ShapeList;
    public var length(flibget_length,never):Int;
    #if true inline function flibget_length(){
        zpp_inner.valmod();
        if(zpp_inner.zip_length){
            zpp_inner.zip_length=false;
            if(false){
                zpp_inner.user_length=0;
                {
                    var cx_ite=zpp_inner.inner.begin();
                    while(cx_ite!=null){
                        var i=cx_ite.elem();
                        if(true)zpp_inner.user_length++;
                        cx_ite=cx_ite.next;
                    }
                };
            }
            else zpp_inner.user_length=zpp_inner.length;
        }
        return zpp_inner.user_length;
    }
    #else inline function flibget_length(){
        return zpp_gl();
    }
    public function zpp_gl(){
        zpp_inner.valmod();
        if(zpp_inner.zip_length){
            zpp_inner.zip_length=false;
            if(false){
                zpp_inner.user_length=0;
                {
                    var cx_ite=zpp_inner.inner.begin();
                    while(cx_ite!=null){
                        var i=cx_ite.elem();
                        if(true)zpp_inner.user_length++;
                        cx_ite=cx_ite.next;
                    }
                };
            }
            else zpp_inner.user_length=zpp_inner.length;
        }
        return zpp_inner.user_length;
    }
    public function zpp_vm(){
        zpp_inner.valmod();
    }
    #end#if swc@:keep#end public static inline function fromArray(array:#if flash9 Array<Dynamic>#else Array<Shape>#end){
        var ret=new ShapeList();
        for(i in array){
            #if flash9 if(!Std.is(i,Shape))throw "Error: Array contains non "+"Shape"+" types.";
            #end ret.push(i);
        }
        return ret;
    }
    #if flash9#if swc@:keep#end public static inline function fromVector(vector:flash.Vector<Shape>){
        var ret=new ShapeList();
        for(i in vector)ret.push(i);
        return ret;
    }
    #end#if swc@:keep#end public inline function at(index:Int):Shape{
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end if(index<0||index>=length)throw "Error: Index out of bounds";
        if(zpp_inner.reverse_flag)index=(length-1-index);
        if(!false){
            if(index<zpp_inner.at_index||zpp_inner.at_ite==null){
                zpp_inner.at_index=index;
                zpp_inner.at_ite=zpp_inner.inner.iterator_at(index);
            }
            else{
                while(zpp_inner.at_index!=index){
                    zpp_inner.at_index++;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
        }
        else{
            if(index<zpp_inner.at_index||zpp_inner.at_ite==null){
                zpp_inner.at_index=0;
                zpp_inner.at_ite=zpp_inner.inner.begin();
                while(true){
                    var x=zpp_inner.at_ite.elem();
                    if(true)break;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
            while(zpp_inner.at_index!=index){
                zpp_inner.at_index++;
                zpp_inner.at_ite=zpp_inner.at_ite.next;
                while(true){
                    var x=zpp_inner.at_ite.elem();
                    if(true)break;
                    zpp_inner.at_ite=zpp_inner.at_ite.next;
                }
            }
        }
        return zpp_inner.at_ite.elem().outer;
    }
    #if swc@:keep#end public inline function push(obj:Shape){
        if(zpp_inner.immutable)throw "Error: "+"Shape"+"List is immutable";
        zpp_inner.modify_test();
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var cont=if(zpp_inner.adder!=null)zpp_inner.adder(obj)else true;
        if(cont){
            if(zpp_inner.reverse_flag)zpp_inner.inner.add(obj.zpp_inner);
            else{
                if(zpp_inner.push_ite==null)zpp_inner.push_ite=empty()?null:zpp_inner.inner.iterator_at(length-1);
                zpp_inner.push_ite=zpp_inner.inner.insert(zpp_inner.push_ite,obj.zpp_inner);
            }
            zpp_inner.invalidate();
            if(zpp_inner.post_adder!=null)zpp_inner.post_adder(obj);
        }
        return cont;
    }
    #if swc@:keep#end public inline function unshift(obj:Shape){
        if(zpp_inner.immutable)throw "Error: "+"Shape"+"List is immutable";
        zpp_inner.modify_test();
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var cont=if(zpp_inner.adder!=null)zpp_inner.adder(obj)else true;
        if(cont){
            if(zpp_inner.reverse_flag){
                if(zpp_inner.push_ite==null)zpp_inner.push_ite=empty()?null:zpp_inner.inner.iterator_at(length-1);
                zpp_inner.push_ite=zpp_inner.inner.insert(zpp_inner.push_ite,obj.zpp_inner);
            }
            else zpp_inner.inner.add(obj.zpp_inner);
            zpp_inner.invalidate();
            if(zpp_inner.post_adder!=null)zpp_inner.post_adder(obj);
        }
        return cont;
    }
    #if swc@:keep#end public inline function pop():Shape{
        if(zpp_inner.immutable)throw "Error: "+"Shape"+"List is immutable";
        zpp_inner.modify_test();
        if(empty())throw "Error: Cannot remove from empty list";
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=null;
        if(zpp_inner.reverse_flag){
            ret=zpp_inner.inner.front();
            var retx=ret.outer;
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.pop();
        }
        else{
            if(zpp_inner.at_ite!=null&&zpp_inner.at_ite.next==null)zpp_inner.at_ite=null;
            var ite=length==1?null:zpp_inner.inner.iterator_at(length-2);
            ret=ite==null?zpp_inner.inner.front():ite.next.elem();
            var retx=ret.outer;
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.erase(ite);
        }
        zpp_inner.invalidate();
        var retx=ret.outer;
        return retx;
    }
    #if swc@:keep#end public inline function shift():Shape{
        if(zpp_inner.immutable)throw "Error: "+"Shape"+"List is immutable";
        zpp_inner.modify_test();
        if(empty())throw "Error: Cannot remove from empty list";
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=null;
        if(zpp_inner.reverse_flag){
            if(zpp_inner.at_ite!=null&&zpp_inner.at_ite.next==null)zpp_inner.at_ite=null;
            var ite=length==1?null:zpp_inner.inner.iterator_at(length-2);
            ret=ite==null?zpp_inner.inner.front():ite.next.elem();
            var retx=ret.outer;
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.erase(ite);
        }
        else{
            ret=zpp_inner.inner.front();
            var retx=ret.outer;
            if(zpp_inner.subber!=null)zpp_inner.subber(retx);
            zpp_inner.inner.pop();
        }
        zpp_inner.invalidate();
        var retx=ret.outer;
        return retx;
    }
    #if swc@:keep#end public inline function add(obj:Shape)return if(zpp_inner.reverse_flag)push(obj)else unshift(obj)#if swc@:keep#end public inline function remove(obj:Shape):Bool{
        if(zpp_inner.immutable)throw "Error: "+"Shape"+"List is immutable";
        zpp_inner.modify_test();
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end var ret=({
            var ret=false;
            {
                var cx_ite=zpp_inner.inner.begin();
                while(cx_ite!=null){
                    var x=cx_ite.elem();
                    {
                        if(x==obj.zpp_inner){
                            ret=true;
                            break;
                        }
                    };
                    cx_ite=cx_ite.next;
                }
            };
            ret;
        });
        if(ret){
            if(zpp_inner.subber!=null)zpp_inner.subber(obj);
            zpp_inner.inner.remove(obj.zpp_inner);
            zpp_inner.invalidate();
        }
        return ret;
    }
    #if swc@:keep#end public inline function empty(){
        #if true if(false)return length==0;
        else return zpp_inner.inner.empty();
        #else return length==0;
        #end
    }
    #if swc@:keep#end public inline function iterator(){
        #if true zpp_inner.valmod();
        #else zpp_vm();
        #end if(zpp_inner.iterators==null)zpp_inner.iterators=new ZNPList_ShapeIterator();
        return ShapeIterator.get(this);
    }
    #if swc@:keep#end public inline function copy(?deep:Bool=false){
        var ret=new ShapeList();
        for(i in this)ret.push(deep?{
            throw "Error: "+"Shape"+" is not a copyable type";
        }
        :i);
        return ret;
    }
    public function new(){
        zpp_inner=new ZPP_ShapeList();
        zpp_inner.outer=this;
    }
    @:keep public inline function toString(){
        var ret="[";
        var fst=true;
        for(i in this){
            if(!fst)ret+=",";
            ret+=i.toString();
            fst=false;
        }
        return ret+"]";
    }
}
#if swc@:keep#end class ShapeIterator{
    public var zpp_inner:ShapeList;
    public var zpp_i:Int;
    public var zpp_critical:Bool;
    public static var zpp_pool:ShapeIterator;
    public var zpp_next:ShapeIterator;
    public function new(){
        if(!ZPP_ShapeList.internal)throw "Error: Cannot instantiate "+"Shape"+"Iterator derp!";
    }
    public static inline function get(list:ShapeList){
        var ret=if(zpp_pool==null){
            ZPP_ShapeList.internal=true;
            var ret=new ShapeIterator();
            ZPP_ShapeList.internal=false;
            ret;
        }
        else{
            var r=zpp_pool;
            zpp_pool=r.zpp_next;
            r;
        }
        ret.zpp_i=0;
        ret.zpp_inner=list;
        ret.zpp_critical=false;
        list.zpp_inner.iterators.add(ret);
        return ret;
    }
    #if swc@:keep#end public inline function hasNext(){
        #if true zpp_inner.zpp_inner.valmod();
        #else zpp_inner.zpp_vm();
        #end var length=zpp_inner.length;
        zpp_critical=true;
        if(zpp_i<length){
            return true;
        }
        else{
            zpp_next=zpp_pool;
            zpp_pool=this;
            zpp_inner.zpp_inner.iterators.remove(this);
            return false;
        }
    }
    #if swc@:keep#end public inline function next(){
        zpp_critical=false;
        return zpp_inner.at(zpp_i++);
    }
}
#if swc@:keep#end class Shape{
    public var zpp_inner:ZPP_Shape;
    public var type(flibget_type,never):ShapeType;
    inline function flibget_type()return[ShapeType.CIRCLE,ShapeType.POLYGON][zpp_inner.type]public var id(flibget_id,never):Int;
    inline function flibget_id()return zpp_inner.id#if swc@:keep#end public inline function isCircle()return zpp_inner.isCircle()#if swc@:keep#end public inline function isPolygon()return zpp_inner.isPolygon()function new(){
        throw "Error: Shape cannot be instantiated derp!";
    }
    public var userData:Dynamic;
    public var body(flibget_body,flibset_body):Body;
    inline function flibget_body(){
        if(zpp_inner.body!=null)return zpp_inner.body.outer;
        else return null;
    }
    inline function flibset_body(body:Body){
        if(this.body!=body){
            if(zpp_inner.body!=null)this.body.shapes.remove(this);
            if(body!=null)body.shapes.add(this);
        }
        return body;
    }
    public var circle(flibget_circle,never):Circle;
    inline function flibget_circle(){
        if(isCircle())return zpp_inner.circle.outer_zn;
        else return null;
    }
    public var polygon(flibget_polygon,never):Polygon;
    inline function flibget_polygon(){
        if(isPolygon())return zpp_inner.polygon.outer_zn;
        else return null;
    }
    public var worldCOM(flibget_worldCOM,never):Vec2;
    inline function flibget_worldCOM(){
        if(zpp_inner.wrap_worldCOM==null){
            zpp_inner.wrap_worldCOM=new Vec2(zpp_inner.worldCOMx,zpp_inner.worldCOMy);
            zpp_inner.wrap_worldCOM.zpp_inner.inuse=true;
            zpp_inner.wrap_worldCOM.zpp_inner.immutable=true;
            zpp_inner.wrap_worldCOM.zpp_inner._validate=zpp_inner.getworldCOM;
        }
        return zpp_inner.wrap_worldCOM;
    }
    public var localCOM(flibget_localCOM,flibset_localCOM):Vec2;
    inline function flibget_localCOM(){
        if(zpp_inner.wrap_localCOM==null){
            if(isCircle())zpp_inner.circle.setupLocalCOM();
            if(isPolygon())zpp_inner.polygon.setupLocalCOM();
        }
        return zpp_inner.wrap_localCOM;
    }
    inline function flibset_localCOM(localCOM:Vec2)return this.localCOM.set(localCOM)public var area(flibget_area,never):Float;
    public var inertia(flibget_inertia,never):Float;
    public var angDrag(flibget_angDrag,never):Float;
    inline function flibget_area(){
        zpp_inner.validate_area_inertia();
        return zpp_inner.area;
    }
    inline function flibget_inertia(){
        zpp_inner.validate_area_inertia();
        return zpp_inner.inertia;
    }
    inline function flibget_angDrag(){
        zpp_inner.validate_angDrag();
        return zpp_inner.angDrag;
    }
    public var material(flibget_material,flibset_material):Material;
    inline function flibget_material()return zpp_inner.material.wrapper()inline function flibset_material(material:Material){
        if(material==null)throw "Error: Cannot assign null as Shape material";
        zpp_inner.setMaterial(material.zpp_inner);
        return this.material;
    }
    public var filter(flibget_filter,flibset_filter):InteractionFilter;
    inline function flibget_filter()return zpp_inner.filter.wrapper()inline function flibset_filter(filter:InteractionFilter){
        if(filter==null)throw "Error: Cannot assign null as Shape filter";
        zpp_inner.setFilter(filter.zpp_inner);
        return this.filter;
    }
    public var fluidProperties(flibget_fluidProperties,flibset_fluidProperties):FluidProperties;
    inline function flibget_fluidProperties(){
        if(zpp_inner.fluidProperties==null)this.fluidProperties=new FluidProperties();
        return zpp_inner.fluidProperties.wrapper();
    }
    inline function flibset_fluidProperties(fluidProperties:FluidProperties){
        if(fluidProperties==null)throw "Error: Cannot assign null as Shape fluidProperties, disable fluids by setting fluidEnabled to false";
        zpp_inner.setFluid(fluidProperties.zpp_inner);
        return zpp_inner.fluidProperties.wrapper();
    }
    public var fluidEnabled(flibget_fluidEnabled,flibset_fluidEnabled):Bool;
    inline function flibget_fluidEnabled()return zpp_inner.fluidEnabled inline function flibset_fluidEnabled(fluidEnabled:Bool){
        zpp_inner.fluidEnabled=fluidEnabled;
        if(fluidEnabled&&zpp_inner.fluidProperties==null)this.fluidProperties=new FluidProperties();
        zpp_inner.wake();
        return this.fluidEnabled;
    }
    public var bounds(flibget_bounds,never):AABB;
    inline public function flibget_bounds(){
        return zpp_inner.aabb.wrapper();
    }
    #if swc@:keep#end public function translate(displacement:Vec2){
        if(zpp_inner.body!=null&&zpp_inner.body.space!=null&&zpp_inner.body.isStatic())throw "Error: Cannot modify Shape belonging to a static Object once inside a Space";
        if(displacement==null)throw "Error: Cannot displace Shape by null Vec2";
        if(displacement.lsq()>0){
            if(isCircle())zpp_inner.circle.__translate(displacement.x,displacement.y);
            else zpp_inner.polygon.__translate(displacement.x,displacement.y);
        }
    }
    #if swc@:keep#end public function scale(scalex:Float,scaley:Float){
        if(zpp_inner.body!=null&&zpp_inner.body.space!=null&&zpp_inner.body.isStatic())throw "Error: Cannot modify Shape belonging to a static Object once inside a Space";
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](scalex);
            ret;
            #else Math.isNaN(scalex);
            #end
        })||({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](scaley);
            ret;
            #else Math.isNaN(scaley);
            #end
        }))throw "Error: Cannot scale Shape by NaN";
        if(scalex!=0&&scaley!=0){
            if(isCircle()){
                var d=scalex*scalex-scaley*scaley;
                if(d*d<ZPP_Const.EPSILON){
                    zpp_inner.circle.__scale(scalex,scaley);
                }
                else throw "Error: Cannot perform a non equal scaling on a Circle";
            }
            else zpp_inner.polygon.__scale(scalex,scaley);
        }
    }
    #if swc@:keep#end public function rotate(angle:Float){
        if(zpp_inner.body!=null&&zpp_inner.body.space!=null&&zpp_inner.body.isStatic())throw "Error: Cannot modify Shape belonging to a static Object once inside a Space";
        if(({
            #if flash9 var ret:Bool=untyped __global__["isNaN"](angle);
            ret;
            #else Math.isNaN(angle);
            #end
        }))throw "Error: Cannot rotate Shape by NaN";
        var dr=angle%(2*Math.PI);
        if(dr!=0.0){
            var cos=Math.cos(angle);
            var sin=Math.sin(angle);
            if(isCircle())zpp_inner.circle.__rotate(sin,cos);
            else zpp_inner.polygon.__rotate(sin,cos);
        }
    }
    #if swc@:keep#end public function transform(matrix:Mat23){
        if(zpp_inner.body!=null&&zpp_inner.body.space!=null&&zpp_inner.body.isStatic())throw "Error: Cannot modify Shape belonging to a static Object once inside a Space";
        if(matrix==null)throw "Error: Cannot transform Shape by null matrix";
        if(matrix.singular())throw "Error: Cannot transform Shape by a singular matrix";
        if(isCircle()){
            if(matrix.equiorthogonal()){
                zpp_inner.circle.__transform(matrix);
            }
            else throw "Error: Cannot transform Circle by a non equiorthogonal matrix";
        }
        else zpp_inner.polygon.__transform(matrix);
    }
    #if swc@:keep#end public static function copy(shape:Shape){
        if(shape==null)throw "Error: Cannot copy null Shape";
        return shape.zpp_inner.copy();
    }
    @:keep public inline function toString(){
        var ret=isCircle()?"Circle":"Polygon";
        return ret+"#"+id;
    }
}
